package za.org.jcicada.deviceproxy.impl;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.cfgmngr.SystemSetType;
import za.org.jcicada.comms.IAcceptor;
import za.org.jcicada.confss.impl.ConfSS;
import za.org.jcicada.genss.ServiceURN;
import za.org.jcicada.genss.SystemURN;
import za.org.jcicada.genss.messages.Result;
import za.org.jcicada.genss.sensor.ISensor;
import za.org.jcicada.genss.sensor.SensorURN;

/**
 * Generic device proxy which extends ConfSS. This class can spawn multiple KatcpClient
 * objects to interface with multiple external devices.
 * @author rcrida
 */
public class DeviceProxy extends ConfSS {

    /** Logger object used for logging messages. */
    private static final Log logger = LogFactory.getLog(DeviceProxy.class);

    /** Map of system URNs to client objects. */
    private Map<SystemURN, KatcpClient> deviceMap = new HashMap<SystemURN, KatcpClient>();
    
    /** Dynamic configuration object for this class, contents from CfgMngr. */
    private DeviceProxyDynamicConfiguration deviceProxyDynamic = new DeviceProxyDynamicConfiguration();

    /**
     * Constructor
     * @param systemURN the name of this device proxy object
     * @param uriCgMngrLocation the location of the CfgMngr
     */
    public DeviceProxy(SystemURN systemURN, URI uriCgMngrLocation) throws URISyntaxException {
        super(systemURN, uriCgMngrLocation);
    }
    
    /**
     * Overrides base class shutdown method. It first calls shutdown for each
     * KatcpClient before calling the super class shutdown method.
     */
    @Override
    public void shutdown() {
    	for (KatcpClient client: deviceMap.values()) {
    		client.shutdown();
    	}
    	super.shutdown();
    }

    @Override
    public void replyGetConfiguration(Result result, Object configuration) {
        if (deviceMap.size() == 0) {
            super.replyGetConfiguration(result, configuration);
            deviceProxyDynamic.loadProperties((Properties) configuration);
            try {
                internalSetupDevices(deviceProxyDynamic.deviceURLs);
            } catch (Exception ex) {
                logger.error("Error setting up devices", ex);
            }
        }
        else {
            logger.error("Received configuration when already configured");
        }
    }

    public void replyLocateService(Result arg0, ServiceURN arg1, URI arg2) {
    }

	public void replySystemSet(Result arg0, SystemSetType type, Set<SystemURN> arg1) {
	}

	public void replySensorSet(Result arg0, SystemURN arg1, Set<SensorURN> arg2) {
	}

    /**
     * Configure the acceptors for a particular service.
     * @param urls semicolon separated list of URIs
     * @param acceptListener accept listener for new connections
     * @param serviceURN the service URN
     * @throws Exception
     */
    protected void internalSetupDevices(String urls) throws Exception {
        for (String url : urls.split(";")) {
            URI deviceURI = new URI(url);
            KatcpClient client = new KatcpClient(new KatcpClient.ISensorCallback() {

                public void addSensor(SystemURN systemURN, ISensor sensor) {
                    try {
                        DeviceProxy.this.addSensor(systemURN, sensor);
                    } catch (URISyntaxException ex) {
                        logger.error("Can't add sensor", ex);
                    }
                }

                public void removeSensor(ISensor sensor) {
                    // TODO Auto-generated method stub
                    
                }
                
            }, deviceURI);
            client.initialize();
            SystemURN deviceURN = new SystemURN(deviceURI.getPath().substring(1));
            deviceMap.put(deviceURN, client);
            // tell life cycle server about new system so that it can foward requests
            lifeCycleServer.addSystem(deviceURN, client);
            // TODO how often should the system be registered?
            this.confssProcessing.addSystemToRegister(client.getSystemURN());
            // now register that the genss address is used for lifecycle requests
            for (IAcceptor acceptor: getServiceAcceptors(getLifeCycleServiceURN())) {
            	registerService(new ServiceURN(deviceURN, "lifecycle"), acceptor.getBaseLocationURI());
            }
            // now register that the genss address is used for sensor requests
            for (IAcceptor acceptor: getServiceAcceptors(getSensorsServiceURN())) {
            	registerService(new ServiceURN(deviceURN, "sensors"), acceptor.getBaseLocationURI());
            }
        }
    }
    
	/**
	 * Main function takes no arguments. A cfgmngr knows what it subsystemURI
	 * will be since there can be only one!
	 * 
	 * @param args
	 *            the command line arguments
	 */
	public static void main(String args[]) {
		String[] confssArgs = { DeviceProxy.class.getCanonicalName(), args[0], args[1] };
		ConfSS.main(confssArgs);
	}

}
